/*****************************************************************************/
/**
 *  @file   Viewer.cpp
 *  @author Naohisa Sakamoto
 */
/*----------------------------------------------------------------------------
 *
 *  Copyright (c) Visualization Laboratory, Kyoto University.
 *  All rights reserved.
 *  See http://www.viz.media.kyoto-u.ac.jp/kvs/copyright/ for details.
 *
 *  $Id$
 */
/*****************************************************************************/
#include "Viewer.h"
#include "Argument.h"
#include <iostream>
#include <string>
#include <util/SPHFileList.h>
#include <util/ImportStaggeredSPHFile.h>
#include <kvs/Math>
#include <kvs/glut/Application>
#include <kvs/glut/Screen>
#include <kvs/Indent>
#include <kvs/Directory>
#include <kvs/Bounds>
#include <kvs/PointObject>
#include <kvs/LineObject>


namespace
{

/*===========================================================================*/
/**
 *  @brief  Check whether the specified name is a dirctory or nor.
 *  @param  name [in] filename or directory name
 *  @return true if the specified name is a directory
 */
/*===========================================================================*/
bool IsDirectory( const std::string& name )
{
    kvs::Directory dir( name );
    return dir.isDirectory();
}

/*===========================================================================*/
/**
 *  @brief  Returns a vector of 3 floating-point numbers.
 *  @param  v [in] vector of 3 double-precision floating-point numbers
 *  @return vector of 3 floating-point numbers
 */
/*===========================================================================*/
kvs::Vec3 ToVec3( const kvs::Vector3d& v )
{
    return kvs::Vec3( (float)v.x(), (float)v.y(), (float)v.z() );
}

/*===========================================================================*/
/**
 *  @brief  Returns a vector of minimum components.
 *  @param  a [in] vector 1
 *  @param  b [in] vector 2
 *  @return minimum components vector
 */
/*===========================================================================*/
kvs::Vec3 Min( const kvs::Vec3& a, const kvs::Vec3& b )
{
    return kvs::Vec3(
        kvs::Math::Min( a[0], b[0] ),
        kvs::Math::Min( a[1], b[1] ),
        kvs::Math::Min( a[2], b[2] ) );
}

/*===========================================================================*/
/**
 *  @brief  Returns a vector of maximum components.
 *  @param  a [in] vector 1
 *  @param  b [in] vector 2
 *  @return maximum components vector
 */
/*===========================================================================*/
kvs::Vec3 Max( const kvs::Vec3& a, const kvs::Vec3& b )
{
    return kvs::Vec3(
        kvs::Math::Max( a[0], b[0] ),
        kvs::Math::Max( a[1], b[1] ),
        kvs::Math::Max( a[2], b[2] ) );
}

/*===========================================================================*/
/**
 *  @brief  Returns a bounding box represented as line object.
 *  @param  region_min [in] minimum coordinate of the box
 *  @param  region_max [in] maximum coordinate of the box
 *  @return bounding box represented as line object
 */
/*===========================================================================*/
kvs::LineObject* Edges( const kvs::Vec3& region_min, const kvs::Vec3& region_max )
{
    kvs::PointObject dummy;
    dummy.setMinMaxObjectCoords( region_min, region_max );
    dummy.setMinMaxExternalCoords( region_min, region_max );

    kvs::Bounds bounds;
    return bounds.outputLineObject( &dummy );
}

void AddEdges( const kvsbloodvis::util::SPHFileList& sph_list, kvs::glut::Screen& screen )
{
    kvs::Vec3 global_region_min = kvs::Vec3::All( kvs::Value<kvs::Real32>::Max() );
    kvs::Vec3 global_region_max = kvs::Vec3::All( kvs::Value<kvs::Real32>::Min() );

    // Register bounding boxes for each sub-volume.
    for ( size_t i = 0; i < sph_list.size(); i++ )
    {
        const kvsbloodvis::util::SPHFile& sph = sph_list.sph(i);
        const kvs::Vec3 local_region_min = ToVec3( sph.org() );
        const kvs::Vec3 local_region_max = ToVec3( sph.org() + sph.max() * sph.pitch() );
        global_region_min = Min( global_region_min, local_region_min );
        global_region_max = Max( global_region_max, local_region_max );

        kvs::LineObject* box = Edges( local_region_min, local_region_max );
        box->setSize( 1.0f );
        box->setColor( kvs::RGBColor::Black() );
        screen.registerObject( box );
    }

    // Register bounding box for whole volume.
    {
        kvs::LineObject* box = Edges( global_region_min, global_region_max );
        box->setSize( 2.0f );
        box->setColor( kvs::RGBColor::Black() );
        screen.registerObject( box );
    }
}

/*
void AddFaces( const kvsbloodvis::util::SPHFileList& sph_list, kvs::glut::Screen& screen )
{
    kvs::Vec3 global_region_min = kvs::Vec3::All( kvs::Value<kvs::Real32>::Max() );
    kvs::Vec3 global_region_max = kvs::Vec3::All( kvs::Value<kvs::Real32>::Min() );

    for ( size_t i = 0; i < sph_list.size(); i++ )
    {
        const kvsbloodvis::util::SPHFile& sph = sph_list.sph(i);

        kvs::Vector3f local_region_min = ::ToVec3( sph.org() );
        kvs::Vector3f local_region_max = ::ToVec3( sph.org() + sph.max() * sph.pitch() );
        global_region_min = ::Min( global_region_min, local_region_min );
        global_region_max = ::Max( global_region_max, local_region_max );

        kvs::StructuredVolumeObject volume = kvsbloodvis::util::ImportSPHFile( sph );
        kvs::PolygonObject* object = new kvs::ExternalFaces( &volume );
        object->setMinMaxExternalCoords( local_region_min, local_region_max );

        kvs::Bounds bounds;
        bounds.setLineWidth( 1.0f );
        bounds.setLineColor( kvs::RGBColor::Red() );

        screen.registerObject( object );
        screen.registerObject( bounds.outputLineObject( object ) );
    }
}
*/

}

/*===========================================================================*/
/**
 *  @brief  Main viewing process.
 *  @param  argc [in] argument count
 *  @param  argv [in] argument values
 *  @return 0 (true) if the process is successfully done.
 */
/*===========================================================================*/
int Viewer::exec( int argc, char** argv )
{
    Argument arg( argc, argv );
    if ( !arg.parse() ) return 1;

    const bool verbose = arg.hasVerboseOption();
    const kvs::Indent indent = kvs::Indent(4);
    if ( verbose ) arg.print( std::cout << "INPUT VARIABLE:" << std::endl, indent );

    kvs::glut::Application app( argc, argv );
    kvs::glut::Screen screen( &app );
    screen.show();

    if ( ::IsDirectory( arg.input() ) )
    {
        const std::string dirname = arg.input();
        const kvsbloodvis::util::SPHFileList sph_list( dirname );
        if ( verbose ) sph_list.print( std::cout << "SPH FILE LIST:" << std::endl, indent );
        ::AddEdges( sph_list, screen );
    }

    return app.run();
}
